﻿/**
 * Copyright (c) 2008 michiyasu wada
 * http://www.seyself.com/
 * 
 * Distributed under The MIT License.
 * [http://www.opensource.org/licenses/mit-license.php]
 */

package demo.modules 
{
import flash.display.DisplayObjectContainer;
import flash.display.Shape;
import flash.display.Sprite;
import flash.display.MovieClip;
import flash.display.Stage;
import flash.events.Event;
import flash.display.DisplayObject;
import flash.events.EventDispatcher;
import flash.events.MouseEvent;
import flash.geom.Point;
import flash.geom.Rectangle;
import demo.modules.parameters.Margin;

public class ScrollBar extends Sprite
{
    
    //--------------------------------------------------------------------------
    //  Properties
    //--------------------------------------------------------------------------
    public var margin:Margin;
    public var targetMargin:Margin;
    public var speed:Number = 1.0;
    private var _target:DisplayObject;
    public function get target():DisplayObject
    { return _target; }
    public function set target( value:DisplayObject ):void
    {
        _target = value;
        if ( _target ) {
            addEventListener( "scroll" , targetScrollHandler );
            var rect:Rectangle = _target.scrollRect || new Rectangle( 0, 0, _target.width, _target.height );
            if ( !_target.mask ) {
                if ( _scrollMask ) {
                    maskRect = rect;
                    _target.mask = _scrollMask;
                } else {
                    var shape:Shape = new Shape();
                    shape.graphics.beginFill(0x000000, 0.0 );
                    shape.graphics.drawRect( _target.x + rect.x, _target.y + rect.y, rect.width, rect.height );
                    shape.graphics.endFill();
                    scrollMask = shape;
                }
            } else {
                maskRect = rect;
            }
            _target.scrollRect = null;
        }
        else {
            removeEventListener( "scroll" , targetScrollHandler );
        }
    }
    
    private var _scrollMask:DisplayObject;
    public function get scrollMask():DisplayObject
    {
        return _scrollMask;
    }
    public function set scrollMask(value:DisplayObject):void
    {
        _scrollMask = value;
        if ( _target ) {
            _target.mask = _scrollMask;
        }
    }
    
    public function get maskRect():Rectangle
    {
        if ( _scrollMask ) {
            var rect:Rectangle = _scrollMask.getRect( _scrollMask );
            return new Rectangle(_scrollMask.x, _scrollMask.y, _scrollMask.width, _scrollMask.height );
        }
        return new Rectangle();
    }
    public function set maskRect(value:Rectangle):void
    {
        if ( _scrollMask ) {
            _scrollMask.x = value.x;
            _scrollMask.y = value.y;
            _scrollMask.width = value.width;
            _scrollMask.height = value.height;
        } else {
            var shape:Shape = new Shape();
            shape.graphics.beginFill(0x000000, 0.0 );
            shape.graphics.drawRect( 0, 0, value.width, value.height );
            shape.graphics.endFill();
            shape.x = value.x;
            shape.y = value.y;
            scrollMask = shape;
        }
    }
    
    public function get scrollType():String
    {
        return _scrollType;
    }
    public function set scrollType( value:String ):void
    {
        if ( value == ScrollType.VERTICAL ) {
            lengthStateName = "height";
            positionStateName = "y";
            _scrollType = ScrollType.VERTICAL;
        } else {
            lengthStateName = "width";
            positionStateName = "x";
            _scrollType = ScrollType.HORIZONTAL;
        }
    }
    
    
    public function get scrollPosition():Number
    {
        var rect:Rectangle = scrollArea;
        var x:Number = dragbar[positionStateName] - rect[positionStateName];
        var y:Number = rect[lengthStateName];
        return x / y;
    }
    public function set scrollPosition( value:Number ):void
    {
        var rect:Rectangle = scrollArea;
        dummyRect[positionStateName] = value * rect[lengthStateName] + rect[positionStateName];
        dragbar[positionStateName] = dummyRect[positionStateName];
        dispatchEvent( new Event( "scroll" ) );
    }
    
    public function get background():DisplayObject
    { return _background; }
    public function set background(value:DisplayObject):void
    {
        _background = value;
        _background.x = _background.x >> 0;
        _background.y = _background.y >> 0;
        _background.addEventListener( "click", bgClickHandler );
    }
    
    public function get dragbar():DisplayObject
    { return _dragbar; }
    public function set dragbar(value:DisplayObject):void
    {
        _dragbar = value;
        _dragbar.x = _dragbar.x >> 0;
        _dragbar.y = _dragbar.y >> 0;
        _dragbar.addEventListener("change"   , changeHandler );
    }
    
    public function get position():Number
    { return dummyRect[positionStateName]; }
    public function set position(value:Number):void
    {
        var rect:Rectangle = scrollArea;
        dummyRect[positionStateName] = 
            Math.min( rect[positionStateName] + rect[lengthStateName],
                Math.max( rect[positionStateName] , value ));
        _dragbar.addEventListener( "enterFrame" , positionMove );
        _dragbar.dispatchEvent( new Event("change") );
    }
    
    public function get stageObject():Stage
    {
        return stage || _dragbar.stage;
    }
    
    //--------------------------------------------------------------------------
    //  Constructor
    //--------------------------------------------------------------------------
    public function ScrollBar( dragbar:DisplayObject = null, 
        background:DisplayObject = null, scrollType:String="horizontal" ) 
    {
        this.scrollType = scrollType;
        this.margin = new Margin();
        this.targetMargin = new Margin();
        
        this.dragbar = dragbar || getChildByName("dragbarObject");
        this.background = background || getChildByName("backgroundObject");
        
        if(this.dragbar) createDummyRect();
    }
    
    //--------------------------------------------------------------------------
    //  Public Methods
    //--------------------------------------------------------------------------
    public function scroll( rate:Number ):void
    {
        var rect:Rectangle = scrollArea;
        position = rate * rect[lengthStateName] + rect[positionStateName];
    }
    
    public function reset():void
    {
        scroll(0);
        //scrollPosition = 0;
    }
    
    
    //--------------------------------------------------------------------------
    //
    //  P R I V A T E
    //
    //--------------------------------------------------------------------------
    
    private var lengthStateName:String = "width";
    private var positionStateName:String = "x";
    
    private var dummyRect:Sprite;
    private var _scrollType:String = "horizontal";
    private var _dragbar:DisplayObject;
    private var _background:DisplayObject;
    
    
    //--------------------------------------------------------------------------
    //  Private Methods
    //--------------------------------------------------------------------------
    private function createDummyRect()
    {
        dummyRect = new Sprite();
        dummyRect.graphics.beginFill( 0x000000, 0.0 );
        dummyRect.graphics.drawRect( 0 , 0, dragbar.width, dragbar.height );
        dummyRect.graphics.endFill();
        dummyRect.buttonMode = true;
        dummyRect.addEventListener( MouseEvent.MOUSE_DOWN , mouseDownHandler );
        dummyRect.addEventListener( MouseEvent.MOUSE_UP , mouseUpHandler );
        dummyRect.x = dragbar.x;
        dummyRect.y = dragbar.y;
        previousPosition = dummyRect.x;
        _dragbar.parent.addChild( dummyRect );
    }
    
    private function get scrollArea():Rectangle
    {
        if( _scrollType == ScrollType.HORIZONTAL ){
            return new Rectangle( 
                    background.x + margin.x, dragbar.y, 
                    background.width - dragbar.width - margin.width, 0
                );
        }
        else if( _scrollType == ScrollType.VERTICAL ){
            return new Rectangle( 
                    dragbar.x, background.y + margin.y, 
                    0, background.height - dragbar.height - margin.height
                );
        }
        return new Rectangle();
    }
    
    private function changeHandler( event:Event ):void
    {
        dispatchEvent( new Event( "scroll" ) );
    }
    
    private function bgClickHandler( event:MouseEvent ):void
    {
        var rect:Rectangle = scrollArea;
        var scale:Point = new Point(event.target.scaleX, event.target.scaleY);
        var pos:Point = new Point( event.localX * scale.x, event.localY * scale.y );
        position = rect[positionStateName] + pos[positionStateName] - ( dummyRect[lengthStateName] / 2 );
    }
    
    private function mouseDownHandler( event:MouseEvent ):void
    {
        var bar = dragbar;
        var param = positionStateName;
        event.target.startDrag( false , scrollArea );
        event.target.addEventListener( "enterFrame" , positionMerge );
        stageObject.addEventListener( "mouseUp" , mouseUpHandler );
    }
    
    private function mouseUpHandler( event:MouseEvent ):void
    {
        mouseReleaseHandler( event );
    }
    
    private var previousPosition:Number = 0;
    private function positionMerge( event:Event ):void
    {
        if( previousPosition != position ){
            dragbar[positionStateName] = dummyRect[positionStateName];
            dispatchEvent( new Event( "scroll" ) );
            previousPosition = position;
        }
    }
    
    private function positionMove( event:Event ):void
    {
        dragbar[positionStateName] += 
            ( dummyRect[positionStateName] - dragbar[positionStateName] ) * speed;
        if (Math.abs(dummyRect[positionStateName] - dragbar[positionStateName]) < 0.5) {
            dragbar[positionStateName] = dummyRect[positionStateName];
            event.target.removeEventListener( event.type, arguments.callee );
        }
        previousPosition = position;
        dispatchEvent( new Event( "scroll" ) );
    }
    
    private function mouseReleaseHandler( event:MouseEvent ):void
    {
        dummyRect.stopDrag();
        dummyRect.removeEventListener( "enterFrame" , positionMerge );
        stageObject.removeEventListener( "mouseUp" , mouseUpHandler );
    }
    
    private function targetScrollHandler( event:Event=null ):void
    {
        if ( target.scrollRect ) target = target;
        var rect:Rectangle = target.getBounds(target);
        var mask:Rectangle = maskRect;
        var dis:Number = rect[lengthStateName] + targetMargin[lengthStateName] - mask[lengthStateName];
        if ( dis > 0 ) {
            var a:Number = targetMargin[positionStateName] + mask[positionStateName];
            var b:Number = rect[positionStateName] - dis * scrollPosition;
            target[positionStateName] = a + b;
        }
    }
    
}

}

